/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package com.farent.solutions.plugins.stripes.hyperstripes.core;

import com.farent.solutions.plugins.stripes.hyperstripes.core.ajax.AjaxResponseInsert;
import com.farent.solutions.plugins.stripes.hyperstripes.spi.RenderKit;
import com.farent.solutions.plugins.stripes.hyperstripes.spi.WebLibrary;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.xml.namespace.QName;
import org.mvel2.templates.CompiledTemplate;
import org.mvel2.templates.TemplateCompiler;
import org.mvel2.templates.TemplateRuntime;

/**
 *
 * @author farouka
 */
public class ComponentProcessor {

    public static String processComponentChildrenContent(ComponentModel rootModel, Object bindings) {
        StringBuilder builder = new StringBuilder();
        List<ComponentModel> children = rootModel.getChildren();
        for (int i = 0; i < children.size(); i++) {
            ComponentModel componentModel1 = children.get(i);

            String tagWithPrefix = componentModel1.getName();
            if (tagWithPrefix.contains(":")) {
                //            tagWithPrefix = tagWithPrefix.
                String[] split = tagWithPrefix.split(":");
                tagWithPrefix = split[1];
            }
            StripesApplicationContext applicationContext =
                    rootModel.getComponentTypeStateHelper().getHsContext();
            ComponentTagHandler tagHandlerForTag =
                    ComponentTagHandlerFactory.getInstance().
                    getTagHandlerForTag(new QName(tagWithPrefix), applicationContext);
            if (tagHandlerForTag != null) {
                String ouput = tagHandlerForTag.createContent(componentModel1, bindings);
                builder.append(ouput);
            }
        }
        return builder.toString();
    }

    public static AjaxResponseInsert processComponentAjaxInsert(ComponentModel model, Object bindings) {
        AjaxResponseInsert ouput = null;
        String tagWithPrefix = model.getName();
        if (tagWithPrefix.contains(":")) {
            String[] split = tagWithPrefix.split(":");
            tagWithPrefix = split[1];
        }
        StripesApplicationContext applicationContext =
                model.getComponentTypeStateHelper().getHsContext();
        ComponentTagHandler tagHandlerForTag =
                ComponentTagHandlerFactory.getInstance().
                getTagHandlerForTag(new QName(tagWithPrefix), applicationContext);
        if (tagHandlerForTag != null) {
            ouput = tagHandlerForTag.createAjaxInsert(model, bindings);
        }
        return ouput;
    }

    public static String processComponentContent(ComponentModel model, Object bindings) {
        StringBuilder builder = new StringBuilder();
        String tagWithPrefix = model.getName();
        if (tagWithPrefix.contains(":")) {
            String[] split = tagWithPrefix.split(":");
            tagWithPrefix = split[1];
        }
        StripesApplicationContext applicationContext =
                model.getComponentTypeStateHelper().getHsContext();
        ComponentTagHandler tagHandlerForTag =
                ComponentTagHandlerFactory.getInstance().
                getTagHandlerForTag(new QName(tagWithPrefix), applicationContext);
        if (tagHandlerForTag != null) {
            String ouput = tagHandlerForTag.createContent(model, bindings);
            builder.append(ouput);
        }
        return builder.toString();
    }

    public static String processComponentContent2(ComponentModel model, 
            Object bindings, OutputStream outputStream) {
        StringBuilder builder = new StringBuilder();
        String tagWithPrefix = model.getName();
        if (tagWithPrefix.contains(":")) {
            String[] split = tagWithPrefix.split(":");
            tagWithPrefix = split[1];
        }
        StripesApplicationContext applicationContext =
                model.getComponentTypeStateHelper().getHsContext();
        ComponentTagHandler tagHandlerForTag =
                ComponentTagHandlerFactory.getInstance().
                getTagHandlerForTag(new QName(tagWithPrefix), applicationContext);
        if (tagHandlerForTag != null) {
            String ouput = tagHandlerForTag.createContent(model, bindings);
            builder.append(ouput);
        }
        return builder.toString();
    }

    /**
     * Creates the script tags required by the components in this model
     * @param model
     * @return
     */
    public String processComponentScripts(ComponentModel model, Object bindings) {
        StringBuilder builder = new StringBuilder();
        String tagWithPrefix = model.getName();
        if (tagWithPrefix.contains(":")) {
            String[] split = tagWithPrefix.split(":");
            tagWithPrefix = split[1];
        }
        StripesApplicationContext applicationContext =
                model.getComponentTypeStateHelper().getHsContext();
        ComponentTagHandler tagHandlerForTag =
                ComponentTagHandlerFactory.getInstance().
                getTagHandlerForTag(new QName(tagWithPrefix), applicationContext);
        if (tagHandlerForTag != null) {
            String ouput = tagHandlerForTag.createScripts(model);
            builder.append(ouput);
        }
        return builder.toString();
    }

    /**
     * Creates the tags required by the components in this model to
     * be present in the head section of the generated HTMl
     * @param model
     * @return
     */
    public static String processComponentHeaders(Map<Class, ComponentTypeStateHelper> stateHelpers) {
        StringBuilder builder = new StringBuilder();
        List<ComponentTypeStateHelper> stateHelpers1List = new ArrayList<ComponentTypeStateHelper>(stateHelpers.values());
        Map<StripesApplicationContext, RenderKit> renderKits = getUniqueRenderKits(stateHelpers1List);
        Set<StripesApplicationContext> keys = renderKits.keySet();
        for (Iterator<StripesApplicationContext> it = keys.iterator(); it.hasNext();) {
            StripesApplicationContext hsCtx = it.next();
            RenderKit kit = renderKits.get(hsCtx);
            List<WebLibrary> webLibraries = kit.getWebLibraries();
            for (int j = 0; j < webLibraries.size(); j++) {
                WebLibrary webLibrary = webLibraries.get(j);
                builder.append(webLibrary.createHeaders(hsCtx));
            }
        }
        return builder.toString();
    }

    private static Map<StripesApplicationContext, RenderKit> getUniqueRenderKits(List<ComponentTypeStateHelper> stateHelpers) {
        Map<StripesApplicationContext, RenderKit> kits = new HashMap<StripesApplicationContext, RenderKit>();
        for (int i = 0; i < stateHelpers.size(); i++) {
            ComponentTypeStateHelper stateHelper = stateHelpers.get(i);
            RenderKit renderkit = stateHelper.getRenderkit();
            kits.put(stateHelper.getHsContext(), renderkit);
        }
        return kits;
    }

    public static String processComponentChildrenScripts(ComponentModel rootModel, Object bindings) {
        StringBuilder builder = new StringBuilder();
        List<ComponentModel> children = rootModel.getChildren();
        for (int i = 0; i < children.size(); i++) {
            ComponentModel componentModel1 = children.get(i);

            String tagWithPrefix = componentModel1.getName();
            if (tagWithPrefix.contains(":")) {
                //            tagWithPrefix = tagWithPrefix.
                String[] split = tagWithPrefix.split(":");
                tagWithPrefix = split[1];
            }
            StripesApplicationContext applicationContext =
                    rootModel.getComponentTypeStateHelper().getHsContext();
            ComponentTagHandler tagHandlerForTag =
                    ComponentTagHandlerFactory.getInstance().
                    getTagHandlerForTag(new QName(tagWithPrefix), applicationContext);
            if (tagHandlerForTag != null) {
                String ouput = tagHandlerForTag.createScripts(componentModel1);
                builder.append(ouput);
            }
        }
        return builder.toString();
    }

    /**
     * Finds a component with the given id in the children of the parent component specified
     * @param uniqueId id of the component to find
     * @param parent the parent of whom the component with the specified id is a child
     * @return child component if found or null
     *
     */
    public static ComponentModel findComponentModel(String uniqueId, ComponentModel parent) {
        return findComponentModel(uniqueId, parent, true);
    }

    /**
     * Finds a component with the given id in the children of the parent component specified
     * @param uniqueId id of the component to find
     * @param parent the parent of whom the component with the specified id is a child
     * @param hsContext the context used for providing the lookup of componentIds. Must NOT be null
     * @return child component if found or null
     *
     */
    public static ComponentModel findComponentModel(String uniqueId,
            ComponentModel parent, StripesApplicationContext hsContext) {
        Map<String, String> componentKeyMap = hsContext.getComponentKeyMap();
        String componentId = componentKeyMap.get(uniqueId);
        if (componentId != null && !componentId.isEmpty()) {
            return findComponentModelByComponentID(componentId, parent);
        }
        //just a fallback incase the componentKeyMap has no path for it
        return findComponentModel(uniqueId, parent, true);
    }

    /**
     * Finds a component with the given id in the children of the parent component specified
     * @param id id of the component to find
     * @param parent the parent of whom the component with the specified id is a child
     * @param recurse Whether the search should recurse through the children
     * @return child component if found or null
     *
     */
    public static ComponentModel findComponentModel(String id, ComponentModel parent, boolean recurse) {
        if (parent == null) {
            return null;
        }
        //check if its looking for itself and return if so
        if (parent.getId() != null && parent.getId().equals(id)) {
            return parent;
        }
        ComponentModel foundModel = null;
        List<ComponentModel> children = parent.getChildren();
        for (int i = 0; i < children.size(); i++) {
            ComponentModel childModel = children.get(i);
            String attributeValue = getAttributeValue(childModel, "id");
            String idValue = childModel.getId();
            if ((idValue != null && idValue.equalsIgnoreCase(id))
                    || (attributeValue != null && attributeValue.equalsIgnoreCase(id))) {
                return childModel;
            }

            //Will only come here if it has not returned
            if (recurse) {
                if (childModel.getChildren() != null && !childModel.getChildren().isEmpty()) {
                    foundModel = findComponentModel(id, childModel);
                }
            }

            if (foundModel != null) {
                String foundId = foundModel.getId();
                String foundAttributeValue = getAttributeValue(childModel, "id");

                if ((foundId != null && foundId.equalsIgnoreCase(id))
                        || (foundAttributeValue != null && foundAttributeValue.equalsIgnoreCase(id))) {
                    return foundModel;
                }
            }
        }
        return foundModel;
    }

    /**
     * Finds a component with the given id in the children of the parent component specified
     * using a space delimited path.
     * 
     * @param componendId componentId of the component. MUST be of the format parentId:childId
     * @param parent the starting Point for the search within the component tree
     * @param path the colon delimited path to the component
     * @return child component if found or null if any of the Ids in the path returns null
     *
     */
    public static ComponentModel findComponentModelByComponentID(String componendId, ComponentModel parent) {
        if (componendId == null || componendId.isEmpty()) {
            return null;
        }
        String idToFind = getRightMostComponentId(componendId);

        //If there is only one component in the path, then just find it as normally done
        if (idToFind.equals(componendId)) {
            return findComponentModel(idToFind, parent);
        }
        String pathToParent = getParentPathOfId(componendId, idToFind);

        //Find Parent
        ComponentModel parentFound = findComponentModelByComponentID(pathToParent, parent);

        //If parent is null, path is invalid. return
        if (parentFound == null) {
            return null;
        }
        return parentFound.getChild(idToFind);
    }

    public static String getAttributeValue(ComponentModel component, String attributeKey) {
        List<Attribute> attributes = component.getAttributes();
        for (int i = 0; i < attributes.size(); i++) {
            Attribute attribute = attributes.get(i);
            if (attribute.getName().equalsIgnoreCase(attributeKey)) {
                return attribute.getValue();
            }
        }
        return null;
    }

    public static String getRightMostComponentId(String componentPath) {
        int lastColon = componentPath.lastIndexOf(":");
        if (lastColon == -1) {
            return componentPath;
        }
        return componentPath.substring(lastColon + 1);
    }

    public static String getParentPathOfId(String componentPath, String uniqueId) {
        int lastColon = componentPath.lastIndexOf(":");
        if (lastColon == -1) {
            return null;
        }
        return componentPath.substring(0, lastColon);
    }

    public static void processComponentContent3(ComponentModel model,
            Object object, OutputStream stream) {
        try {

            InputStream template3 = ComponentProcessor.class.getResourceAsStream("/dojoFormTable.tag");
            Map vars = new HashMap();
            vars.put("model", model);
            vars.put("stream", stream);
            CompiledTemplate compiled3 = TemplateCompiler.compileTemplate(template3);
            TemplateRuntime.execute(compiled3, object, vars, stream);
        } catch (Exception e) {
            e.printStackTrace(System.err);
        }
    }
}
